wm(halt), /* the symbol 'halt' is added to working memory */
nl,
write('Successful termination.'),
nl. /* ... so inform user accordingly */
forward_chain :-
(rule RuleName forward if LHS then RHS), /* find a rule.... */
all_in_mem(LHS), /* whose left-hand-side patterns are all satisfied...*/
not(already_did(RuleName,LHS)), /* and which we haven't already performed */
perform(RHS), /* then perform associated right-hand-side actions */
assert(already_did(RuleName,LHS)), /*avoid repeating this exact case */
forward_chain. /* then carry on forward-chaining */
forward_chain :- /* this case only reached when above clause fails */
nl,
write('No (more) applicable rules.'),
nl. /* ... so inform user accordingly */
/* A top-level goal fc ensures that working memory is cleared up prior to execution, and places the special symbol 'start' in working memory before invoking the workhorse forward_chain:
*/
fc :- /* top-level invocation*/
abolish(wm, 1), /* clear out working memory*/
assert(wm(start)), /* add special 'start' symbol to working memory*/
abolish(already_did, 2), /* clear flag which prevents duplicate firings*/
During forward chaining, a rule's left-hand-side pattern is said to be
'satisfied' either by being present in working memory or by being
retrievable from frame memory. Working memory elements are stored
internally using the predicate wm, so in the m ost general case we just
need to see whether wm(<pattern>) succeeds. More special cases exist, for
dealing with patterns such as the X of Y is Z, so the first four clauses of
in_mem cater with these cases, while the general case is left for last. */
all_in_mem(First & Rest) :- /* conjunction of left-hand-side patterns*/
in_mem(First), /* see if the first one is satisfied*/
all_in_mem(Rest). /* recursively see if rest are satisfied*/
all_in_mem(X) :- /* singleton pattern*/
not(X = (_ & _)), /* this ensures that it really is just a singleton, not a conjunction*/
in_mem(X). /* see if it is stored in working memory or frame memory*/
in_mem(the Attr of Obj is Val) :- /* patterns of this form require frame access..*/
fetch(Obj, Attr, Val). /* ... so invoke the frame-retrieval workhorse*/
in_mem(X instance_of Y) :- /* this is useful for looking up instance_of relations...*/
(X instance_of Y with _ ). /* in which case we just ignore the details following 'with'*/
in_mem(X subclass_of Y) :- /* this is useful for looking up subclass_of relations...*/
(X subclass_of Y with _). /* in which case we just ignore the details following 'with' */
in_mem(deduce X) :- /* this is how we invoke a backward-chaining rule...*/
prove(X). /* ...in which case we let the workhorse prove do the work*/
in_mem(X) :- /* this is the usual case, i.e. looking for an arbitrary pattern...*/
wm(X). /* just see if it is in the Prolog database in this form.*/
/*
In a 'pure' production system interpreter, the concept of performing right-hand-side actions is restricted to adding or removing elements from working memory. In MIKE, we make this explicit with the operators add and remove, and allow other special
actions as well, such as announce and halt. The first clause below handles
conjunctions of right hand side elements, while the second and third
clauses deal respectively with adding and removing working memory elements.
The fourth clause caters for cosmetic printout routines, and the final
clause (the default case) adds the special symbol halt to working memory
for the benefit of the forward_chain workhorse routine. */
perform(First & Rest) :- /* conjunction of right-hand-side patterns*/
perform(First), /* do the first one (this will involve one of the clauses below)*/